#ifndef PUDGE_UI_BUTTON_HPP
#define PUDGE_UI_BUTTON_HPP

#include <pudge/ui/sprite.hpp>
#include <pudge/ui/widget.hpp>
#include <pudge/grfx/mesh.hpp>
#include <pudge/grfx/color.hpp>

namespace pudge { namespace ui {

	template<class Traits> struct button : widget<Traits>
	{
		struct texture_traits
		{
			typedef pudge::grfx::color<pudge::grfx::RGBA,unsigned char> color_type;
			typedef pudge::grfx::CLAMP_TEXTURE wrap_type;
		};
		struct index_traits
		{
			typedef unsigned long value_type;
			typedef pudge::grfx::DYNAMIC_DRAW usage_type;
			typedef pudge::grfx::QUADS poly_type;
		};
		struct position_traits
		{
			typedef pudge::math::vec<2,float> value_type;
			typedef pudge::grfx::DYNAMIC_DRAW usage_type;
		};
		typedef position_traits tcoord_traits;
		typedef pudge::grfx::texture2d<texture_traits> texture_type;
		typedef sprite<float> sprite_type;
	
		pudge::grfx::index_attribute<index_traits> indices;
		pudge::grfx::vertex_attribute<position_traits> positions;
		pudge::grfx::vertex_attribute<tcoord_traits> tcoords;
		
		/// sprites for texture coords only, button positions are extracted from the ui frame.
		/// This allows for relative vs actual positioning without any kind of parent<->child interface -
		/// making layout much easier and more flexible than some other techniques.

		sprite_type up, down, hover, coords, quad;

		bool pressed;

		button() : pressed(false) {}

		virtual ~button()
		{
			pudge::grfx::decache_attribute(indices);
			pudge::grfx::decache_attribute(positions);
			pudge::grfx::decache_attribute(tcoords);
		}
	};

	/// Function that streams a button's indices, positions, and texture coordinates into the graphics system.

	struct stream_button_
	{
		template<class I, class P, class T, class B> struct result { typedef void type; };
		template<class IStream, class PStream, class TStream, class Button>
		void operator()(IStream & is, PStream & ps, TStream & ts, Button & b) const
		{
			typedef PStream::value_type P;
			typedef TStream::value_type T;

			// stream all 4 indices

			is << 0 << 1 << 2 << 3;

			Button::sprite_type & q = b.quad;

			// stream the quad

			ps	<< P(q.xmin, q.ymax)	// top left
				<< P(q.xmax, q.ymax)	// top right
				<< P(q.xmax, q.ymin)	// bottom right
				<< P(q.xmin, q.ymin);	// bottom left

			// stream texture coords

			Button::sprite_type & d = b.coords;

			ts	<< T(d.xmin, d.ymax)	// top left
				<< T(d.xmax, d.ymax)	// top right
				<< T(d.xmax, d.ymin)	// bottom right
				<< T(d.xmin, d.ymin);	// bottom left
		}
	};

	pudge::meta::lazy<stream_button_> const stream_button = stream_button_();

	/// lazy function that ticks a button.
	
	struct tick_button_
	{
		template<class C, class F, class B> struct result { typedef void type; };
		template<class Context, class Frame, class Button>
		void operator()(Context & con, Frame & f, Button & b) const
		{
			using namespace pudge::meta;

			typedef Context::mouse_event_list MEvents;
			typedef Context::canvas_type Canvas;

			MEvents & mevents = con.mouse_events();
			Canvas & c = con.canvas();

			for(MEvents::iterator i = mevents.begin(); i != mevents.end(); ++i)
			{
				MEvents::value_type & v = *i;

				if(		f.xmin <= v.x && v.x <= f.xmax
					&&	f.ymin <= v.y && v.y <= f.ymax )
				{
					if(v.left_down && c.hot() == NULL)
					{
						b.pressed = true;
						b.coords = b.down;
					}
					else if(v.left_up)
					{
						b.pressed = false;
						b.coords = b.hover;
					}
					else if(v.hover && b.pressed == false)
					{
						b.coords = b.hover;
					}

					if(c.hot() == NULL) c.hot(&b);
				}
				else 
				{
					b.pressed = false;
					b.coords = b.up;
				}
			}

			b.quad = f;

			pudge::grfx::cache_attributes
				(	b.indices
				,	b.positions
				,	b.tcoords
				,	stream_button(_1,_2,_3,ref(b))
				,	4
				,	4 );
		}
	};

	pudge::meta::lazy<tick_button_> const tick_button = tick_button_();

	/// lazy function that draws a button.

	struct draw_button_
	{
		template<class C, class B, class T> struct result { typedef void type; };
		template<class Context, class Button, class Texture>
		void operator()(Context & c, Button & b, Texture & t) const
		{
			pudge::grfx::render_attributes(4, b.indices, b.positions, b.tcoords, t);
		}
	};

	pudge::meta::lazy<draw_button_> const draw_button = draw_button_();
}}

#endif